home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / Reference / Amiga_Mail_Vol2 / Archives / Plain / ja90 / SimpleRexx.txt < prev    next >
Encoding:
Text File  |  1991-01-22  |  27.1 KB  |  1,062 lines

  1.  
  2. SimpleRexx: A Simple ARexx Interface
  3.  
  4. by Michael Sinz
  5.  
  6. The ability to handle scripts is a powerful feature for any
  7. application.  Whenever users have a repetitive, well-defined job to do
  8. with application software, script capabilities allow them to perform
  9. the job automatically without any user intervention.  For example,
  10. many communication programs allow the user to set up a script which
  11. will automatically dial up a host system, login to it, and check for
  12. messages to download.  To encourage the development of more
  13. applications which have this powerful feature, Commodore has added
  14. ARexx to V2.0 of the Amiga system software.
  15.  
  16.  
  17.  
  18. What Is ARexx?
  19.  
  20. ARexx is the Amiga implementation of the REXX language.  Like BASIC,
  21. ARexx is an interpreted language.  ARexx can be used alone for almost
  22. any programming job, but the real power of ARexx is unleashed when it
  23. is used with applications as a system-level scripting language.  With
  24. the addition of its own ARexx port and a small amount of code, an
  25. application can gain all the power scripting capabilities offer.
  26. There are other benefits as well.  The burden of writing code to
  27. handle scripts is reduced significantly.  Also, ARexx helps provide a
  28. consistent interface to the user.  Without it, every developer who
  29. implements scripts must invent their own scripting language, forcing
  30. the user to learn several different scripting languages rather than
  31. just one.
  32.  
  33. Perhaps best of all is that applications which support ARexx can
  34. "talk" to each other by sending commands and sharing data - even if
  35. they are from different vendors.  For instance, a communications
  36. program with an ARexx port could be set up to automatically download
  37. data from a financial bulletin board and then pass the data to a
  38. spreadsheet application to create a graph or financial model.  The
  39. ability of applications to "talk" to one another on a multitasking
  40. computer is known as Inter-process Communication (IPC).
  41.  
  42. Because it interprets scripts and passes strings, ARexx has been
  43. optimized for string processing.  Almost all ARexx interactions
  44. involve passing a string; numbers are even passed as ASCII
  45. representations in most situations.
  46.  
  47. ARexx has enhanced abilities to send and receive control messages to
  48. and from many sources.  An application that supports ARexx can send
  49. and receive these control messages.  The messages contain text strings
  50. that are either interpreted directly by ARexx itself or that are
  51. passed on as commands to be processed by the destination application.
  52.  
  53. ARexx also supplies methods for applications to send and recieve data
  54. through an ARexx port.  The data can be sent in a message or via the
  55. ARexx RVI (Rexx Variable Interface).  In either case, data can be
  56. transferred to and from ARexx.  A complete ARexx supporting
  57. application would need to be able to send data to a requesting ARexx
  58. program/script and get data from that program.
  59.  
  60.  
  61.  
  62. The SimpleRexx Routines
  63.  
  64. Adding an ARexx interface to an application can be difficult,
  65. especially when working with it for the first time.  SimpleRexx was
  66. written to serve not only as an example of how to implement an ARexx
  67. interface but also as a "wrapper" to be incorporated into other code.
  68. It contains routines to take care of the lower level ARexx work. It
  69. can be used to add minimal ARexx support to many applications in a
  70. backwards-compatible fashion.  In other words, an application that
  71. uses SimpleRexx will still work on systems without ARexx (but won't be
  72. able to execute ARexx scripts).
  73.   
  74. The code below consists of SimpleRexx.c, the "wrapper" code that makes
  75. up the ARexx interface, and an example, SimpleRexxExample.c, which
  76. illustrates the use of the wrapper.  The test.rexx script is an
  77. example ARexx script to execute while SimpleRexxExample is running.
  78. It will send commands to SimpleRexxExample in order to control it.
  79. The test.results file shows the output of test.rexx.
  80.  
  81. In addition to the code examples listed here, you will need to install
  82. the rexxsyslib.library and rexxsupport.library in the libs: directory
  83. of the target machine.  Don't forget to give the rexxmast command
  84. either in your startup-sequence or from the CLI in order to start
  85. ARexx.
  86.  
  87.  
  88.  
  89. Overview of Functions
  90.  
  91. The source to SimpleRexx is a single file, SimpleRexx.c.  The header
  92. file, SimpleRexx.h, contains the type definitions and prototypes for
  93. the functions.
  94.  
  95. The SimpleRexx functions are used as follows:
  96.  
  97.     rexx_context=InitARexx(AppBaseName, Extension)
  98.  
  99.     This allocates and initializes a SimpleRexx ARexxContext
  100.     structure.  This context is much like a file handle in
  101.     that it will be used in all other calls that make use of 
  102.     this SimpleRexx context.  Since all SimpleRexx calls
  103.     correctly check the rexx_context before doing work,
  104.     you do not need to check the return result of this call.
  105.     If ARexx is not available on your system, SimpleRexx
  106.     won't do anything. 
  107.  
  108.  
  109.     port_name=ARexxName(rexx_context)
  110.  
  111.     This function returns a pointer to the name of the ARexx
  112.     port's context.  The name is based on the AppBaseName
  113.     plus an invocation number allowing multiple copies of an
  114.     application to run at the same time.  If you have no
  115.     ARexx port, it returns NULL.
  116.  
  117.  
  118.     sigmask=ARexxSignal(rexx_context)
  119.  
  120.     This function returns the signal bit mask of your
  121.     context's ARexx port.  This should be combined with
  122.     other signal masks to produce the argument to the Wait()
  123.     call.  This returns NULL if there is no signal mask.
  124.  
  125.     rmsg=GetARexxMsg(rexx_context)
  126.  
  127.     This function returns the next ARexx message that is
  128.     waiting.  rmsg=NULL if there is no message or ARexx is
  129.     not around.  rmsg=REXX_RETURN_ERROR if a message sent to
  130.     ARexx via SendARexxMsg() returns an error.
  131.  
  132.     ReplyARexxMsg(rexx_context, rmsg, result, error)
  133.  
  134.     This function sends back the ARexx message received via
  135.     GetARexxMsg().  The result field is a pointer to a
  136.     result string that is returned via OPTIONS RESULTS in
  137.     the RESULT ARexx variable.  If you have no result, set
  138.     this to NULL.  Error is the error severity level.  If
  139.     this is 0, the result string will be returned.  If this
  140.     is non-zero, the error level will be returned in RC.
  141.  
  142.     worked=SendARexxMsg(rexx_context, string, StringFileFlag)
  143.  
  144.     This function sends a string to ARexx.  It sets the
  145.     default host to the context and sets the RXFB_STRING bit
  146.     in the message if the StringFileFlag is set.  This
  147.     routine returns FALSE if the message was not sent.
  148.  
  149.     worked=SetARexxLastError(rexx_context, rmsg, ErrorString)
  150.  
  151.     This function uses the RVI (Rexx Variable Interface) to
  152.     set a variable named <AppBaseName>.LASTERROR to the
  153.     ErrorString.  This is where the error message should go
  154.     if there is an error.  This function returns FALSE if
  155.     this fails.  You must call this routine after a
  156.     GetARexxMsg() and before ReplyARexxMsg().
  157.  
  158.     FreeARexx(rexx_context)
  159.  
  160.     This closes a SimpleRexx context received from InitARexx().
  161.  
  162.  
  163.  
  164. The Future of ARexx
  165.  
  166. ARexx is much more than a system-level scripting language.  Among
  167. other things, ARexx can be used like glue to put together a set of
  168. application modules.  This frees the programmer from worrying about
  169. data sharing between modules so that more effort can be put into
  170. refining the code modules themselves. It also allows a user who
  171. understands ARexx to add their own refinements creating a truly custom
  172. environment.  This customizability is also ideal for VARs or dealers
  173. who want to sell vertical market solutions.
  174.  
  175.  
  176.  
  177. #
  178. # MakeFile for SimpleRexx and SimpleRexxExample
  179. #
  180.  
  181. CFLAGS= -b1 -cfist -d0 -ms0 -rr1 -v -w
  182.  
  183. HEAD=    SimpleRexx.h
  184.  
  185. CODE=    SimpleRexx.c SimpleRexxExample.c
  186.  
  187. OBJS=    SimpleRexx.o SimpleRexxExample.o
  188.  
  189. LIBS=    LIB:lcsr.lib rexxvars.o LIB:amiga.lib
  190.  
  191. .c.o:
  192.     @LC $(CFLAGS) $*
  193.  
  194. SimpleRexxExample: $(OBJS) $(LIBS)
  195.     BLink FROM LIB:c.o $(OBJS) TO SimpleRexxExample LIB $(LIBS) SD SC ND
  196.  
  197. SimpleRexx.o: SimpleRexx.c SimpleRexx.h
  198.  
  199. SimpleRexxExample.o: SimpleRexxExample.c SimpleRexx.h
  200.  
  201.  
  202.  
  203. /*
  204.  * file: simplerexx.h
  205.  *
  206.  * Simple ARexx interface...
  207.  *
  208.  * This is a very "Simple" interface...
  209.  */
  210.  
  211. #ifndef    SIMPLE_REXX_H
  212. #define    SIMPLE_REXX_H
  213.  
  214. #include    <exec/types.h>
  215. #include    <exec/nodes.h>
  216. #include    <exec/lists.h>
  217. #include    <exec/ports.h>
  218.  
  219. #include    <rexx/storage.h>
  220. #include    <rexx/rxslib.h>
  221.  
  222. /*
  223.  * This is the handle that SimpleRexx will give you
  224.  * when you initialize an ARexx port...
  225.  *
  226.  * The conditional below is used to skip this if we have
  227.  * defined it earlier...
  228.  */
  229. #ifndef    AREXXCONTEXT
  230.  
  231. typedef void *AREXXCONTEXT;
  232.  
  233. #endif    /* AREXXCONTEXT */
  234.  
  235. /*
  236.  * The value of RexxMsg (from GetARexxMsg) if there was an error returned
  237.  */
  238. #define    REXX_RETURN_ERROR    ((struct RexxMsg *)-1L)
  239.  
  240. /*
  241.  * This function closes down the ARexx context that was opened
  242.  * with InitARexx...
  243.  */
  244. void FreeARexx(AREXXCONTEXT);
  245.  
  246. /*
  247.  * This routine initializes an ARexx port for your process
  248.  * This should only be done once per process.  You must call it
  249.  * with a valid application name and you must use the handle it
  250.  * returns in all other calls...
  251.  *
  252.  * NOTE:  The AppName should not have spaces in it...
  253.  *        Example AppNames:  "MyWord" or "FastCalc" etc...
  254.  *        The name *MUST* be less that 16 characters...
  255.  *        If it is not, it will be trimmed...
  256.  *        The name will also be UPPER-CASED...
  257.  *
  258.  * NOTE:  The Default file name extension, if NULL will be
  259.  *        "rexx"  (the "." is automatic)
  260.  */
  261. AREXXCONTEXT InitARexx(char *,char *);
  262.  
  263. /*
  264.  * This function returns the port name of your ARexx port.
  265.  * It will return NULL if there is no ARexx port...
  266.  *
  267.  * This string is *READ ONLY*  You *MUST NOT* modify it...
  268.  */
  269. char *ARexxName(AREXXCONTEXT);
  270.  
  271. /*
  272.  * This function returns the signal mask that the Rexx port is
  273.  * using.  It returns NULL if there is no signal...
  274.  *
  275.  * Use this signal bit in your Wait() loop...
  276.  */
  277. ULONG ARexxSignal(AREXXCONTEXT);
  278.  
  279. /*
  280.  * This function returns a structure that contains the commands sent from
  281.  * ARexx...  You will need to parse it and return the structure back
  282.  * so that the memory can be freed...
  283.  *
  284.  * This returns NULL if there was no message...
  285.  */
  286. struct RexxMsg *GetARexxMsg(AREXXCONTEXT);
  287.  
  288. /*
  289.  * Use this to return a ARexx message...
  290.  *
  291.  * If you wish to return something, it must be in the RString.
  292.  * If you wish to return an Error, it must be in the Error.
  293.  */
  294. void ReplyARexxMsg(AREXXCONTEXT,struct RexxMsg *,char *,LONG);
  295.  
  296. /*
  297.  * This function will send a string to ARexx...
  298.  *
  299.  * The default host port will be that of your task...
  300.  *
  301.  * If you set StringFile to TRUE, it will set that bit for the message...
  302.  *
  303.  * Returns TRUE if it send the message, FALSE if it did not...
  304.  */
  305. short SendARexxMsg(AREXXCONTEXT,char *,short);
  306.  
  307. /*
  308.  * This function will set an error string for the ARexx
  309.  * application in the variable defined as <appname>.LASTERROR
  310.  *
  311.  * Note that this can only happen if there is an ARexx message...
  312.  *
  313.  * This returns TRUE if it worked, FALSE if it did not...
  314.  */
  315. short SetARexxLastError(AREXXCONTEXT,struct RexxMsg *,char *);
  316.  
  317. #endif    /* SIMPLE_REXX_H */
  318.  
  319.  
  320.  
  321. /* 
  322.  * File: SimpleRexx.c
  323.  *
  324.  * Simple ARexx interface...
  325.  *
  326.  * This is a very "Simple" interface to the world of ARexx...
  327.  * For more complex interfaces into ARexx, it is best that you
  328.  * understand the functions that are provided by ARexx.
  329.  * In many cases they are more powerful than what is presented
  330.  * here.
  331.  *
  332.  * This code is fully re-entrant and self-contained other than
  333.  * the use of SysBase/AbsExecBase and the ARexx RVI support
  334.  * library which is also self-contained...
  335.  */
  336.  
  337. #include    <exec/types.h>
  338. #include    <exec/nodes.h>
  339. #include    <exec/lists.h>
  340. #include    <exec/ports.h>
  341. #include    <exec/memory.h>
  342.  
  343. #include    <proto/exec.h>
  344.  
  345. #include    <rexx/storage.h>
  346. #include    <rexx/rxslib.h>
  347.  
  348. #include    <string.h>
  349. #include    <ctype.h>
  350.  
  351. /*
  352.  * The prototypes for the few ARexx functions we will call...
  353.  */
  354. struct RexxMsg *CreateRexxMsg(struct MsgPort *,char *,char *);
  355. void *CreateArgstring(char *,long);
  356. void DeleteRexxMsg(struct RexxMsg *);
  357. void DeleteArgstring(char *);
  358. BOOL IsRexxMsg(struct Message *);
  359.  
  360. /*
  361.  * Pragmas for the above functions...  (To make this all self-contained...)
  362.  * If you use RexxGlue.o, this is not needed...
  363.  *
  364.  * These are for Lattice C 5.x  (Note the use of RexxContext->RexxSysBase)
  365.  */
  366. #pragma libcall RexxContext->RexxSysBase CreateRexxMsg 90 09803
  367. #pragma libcall RexxContext->RexxSysBase CreateArgstring 7E 0802
  368. #pragma libcall RexxContext->RexxSysBase DeleteRexxMsg 96 801
  369. #pragma libcall RexxContext->RexxSysBase DeleteArgstring 84 801
  370. #pragma libcall RexxContext->RexxSysBase IsRexxMsg A8 801
  371.  
  372. /*
  373.  * Prototypes for the RVI ARexx calls...  (link with RexxVars.o)
  374.  */
  375. __stdargs long CheckRexxMsg(struct RexxMsg *);
  376. __stdargs long GetRexxVar(struct RexxMsg *,char *,char **);
  377. __stdargs long SetRexxVar(struct RexxMsg *,char *,char *,long);
  378.  
  379. /*
  380.  * Now, we have made the pragmas needed, let's get to work...
  381.  */
  382.  
  383. /*
  384.  * A structure for the ARexx handler context
  385.  * This is *VERY* *PRIVATE* and should not be touched...
  386.  */
  387. struct    ARexxContext
  388. {
  389. struct    MsgPort    *ARexxPort;    /* The port messages come in at... */
  390. struct    Library    *RexxSysBase;    /* We will hide the library pointer here... */
  391.     long    Outstanding;    /* The count of outstanding ARexx messages... */
  392.     char    PortName[24];    /* The port name goes here... */
  393.     char    ErrorName[28];    /* The name of the <base>.LASTERROR... */
  394.     char    Extension[8];    /* Default file name extension... */
  395. };
  396.  
  397. #define    AREXXCONTEXT    struct ARexxContext *
  398.  
  399. #include    "SimpleRexx.h"
  400.  
  401. /*
  402.  * This function returns the port name of your ARexx port.
  403.  * It will return NULL if there is no ARexx port...
  404.  *
  405.  * This string is *READ ONLY*  You *MUST NOT* modify it...
  406.  */
  407. char *ARexxName(AREXXCONTEXT RexxContext)
  408. {
  409. register    char    *tmp=NULL;
  410.  
  411.     if (RexxContext) tmp=RexxContext->PortName;
  412.     return(tmp);
  413. }
  414.  
  415. /*
  416.  * This function returns the signal mask that the Rexx port is
  417.  * using.  It returns NULL if there is no signal...
  418.  *
  419.  * Use this signal bit in your Wait() loop...
  420.  */
  421. ULONG ARexxSignal(AREXXCONTEXT RexxContext)
  422. {
  423. register    ULONG    tmp=NULL;
  424.  
  425.     if (RexxContext) tmp=1L << (RexxContext->ARexxPort->mp_SigBit);
  426.     return(tmp);
  427. }
  428.  
  429. /*
  430.  * This function returns a structure that contains the commands sent from
  431.  * ARexx...  You will need to parse it and return the structure back
  432.  * so that the memory can be freed...
  433.  *
  434.  * This returns NULL if there was no message...
  435.  */
  436. struct RexxMsg *GetARexxMsg(AREXXCONTEXT RexxContext)
  437. {
  438. register    struct    RexxMsg    *tmp=NULL;
  439. register        short    flag;
  440.  
  441.     if (RexxContext)
  442.         if (tmp=(struct RexxMsg *)GetMsg(RexxContext->ARexxPort))
  443.     {
  444.         if (tmp->rm_Node.mn_Node.ln_Type==NT_REPLYMSG)
  445.         {
  446.             /*
  447.              * If we had sent a command, it would come this way...
  448.              *
  449.              * Since we don't in this simple example, we just throw
  450.              * away anything that looks "strange"
  451.              */
  452.             flag=FALSE;
  453.             if (tmp->rm_Result1) flag=TRUE;
  454.  
  455.             /*
  456.              * Free the arguments and the message...
  457.              */
  458.             DeleteArgstring(tmp->rm_Args[0]);
  459.             DeleteRexxMsg(tmp);
  460.             RexxContext->Outstanding-=1;
  461.  
  462.             /*
  463.              * Return the error if there was one...
  464.              */
  465.             tmp=flag ? REXX_RETURN_ERROR : NULL;
  466.         }
  467.     }
  468.     return(tmp);
  469. }
  470.  
  471. /*
  472.  * Use this to return a ARexx message...
  473.  *
  474.  * If you wish to return something, it must be in the RString.
  475.  * If you wish to return an Error, it must be in the Error.
  476.  * If there is an error, the RString is ignored.
  477.  */
  478. void ReplyARexxMsg(AREXXCONTEXT RexxContext,struct RexxMsg *rmsg,
  479.             char *RString,LONG Error)
  480. {
  481.     if (RexxContext) if (rmsg) if (rmsg!=REXX_RETURN_ERROR)
  482.     {
  483.         rmsg->rm_Result2=0;
  484.         if (!(rmsg->rm_Result1=Error))
  485.         {
  486.             /*
  487.              * if you did not have an error we return the string
  488.              */
  489.             if (rmsg->rm_Action & (1L << RXFB_RESULT)) if (RString)
  490.             {
  491.                 rmsg->rm_Result2=(LONG)CreateArgstring(RString,
  492.                             (LONG)strlen(RString));
  493.             }
  494.         }
  495.  
  496.         /*
  497.          * Reply the message to ARexx...
  498.          */
  499.         ReplyMsg((struct Message *)rmsg);
  500.     }
  501. }
  502.  
  503. /*
  504.  * This function will set an error string for the ARexx
  505.  * application in the variable defined as <appname>.LASTERROR
  506.  *
  507.  * Note that this can only happen if there is an ARexx message...
  508.  *
  509.  * This returns TRUE if it worked, FALSE if it did not...
  510.  */
  511. short SetARexxLastError(AREXXCONTEXT RexxContext,struct RexxMsg *rmsg,
  512.             char *ErrorString)
  513. {
  514. register    short    OkFlag=FALSE;
  515.  
  516.     if (RexxContext) if (rmsg) if (CheckRexxMsg(rmsg))
  517.     {
  518.         /*
  519.          * Note that SetRexxVar() has more than just a TRUE/FALSE
  520.          * return code, but for this "basic" case, we just care if
  521.          * it works or not.
  522.          */
  523.         if (!SetRexxVar(rmsg,RexxContext->ErrorName,ErrorString,
  524.                         (long)strlen(ErrorString)))
  525.         {
  526.             OkFlag=TRUE;
  527.         }
  528.     }
  529.     return(OkFlag);
  530. }
  531.  
  532. /*
  533.  * This function will send a string to ARexx...
  534.  *
  535.  * The default host port will be that of your task...
  536.  *
  537.  * If you set StringFile to TRUE, it will set that bit for the message...
  538.  *
  539.  * Returns TRUE if it send the message, FALSE if it did not...
  540.  */
  541. short SendARexxMsg(AREXXCONTEXT RexxContext,char *RString,
  542.             short StringFile)
  543. {
  544. register    struct    MsgPort    *RexxPort;
  545. register    struct    RexxMsg    *rmsg;
  546. register        short    flag=FALSE;
  547.  
  548.     if (RexxContext) if (RString)
  549.     {
  550.         if (rmsg=CreateRexxMsg(RexxContext->ARexxPort,
  551.                     RexxContext->Extension,
  552.                     RexxContext->PortName))
  553.         {
  554.             rmsg->rm_Action=RXCOMM | (StringFile ?
  555.                             (1L << RXFB_STRING):0);
  556.             if (rmsg->rm_Args[0]=CreateArgstring(RString,
  557.                             (LONG)strlen(RString)))
  558.             {
  559.                 /*
  560.                  * We need to find the RexxPort and this needs
  561.                  * to be done in a Forbid()
  562.                  */
  563.                 Forbid();
  564.                 if (RexxPort=FindPort(RXSDIR))
  565.                 {
  566.                     /*
  567.                      * We found the port, so put the
  568.                      * message to ARexx...
  569.                      */
  570.                     PutMsg(RexxPort,(struct Message *)rmsg);
  571.                     RexxContext->Outstanding+=1;
  572.                     flag=TRUE;
  573.                 }
  574.                 else
  575.                 {
  576.                     /*
  577.                      * No port, so clean up...
  578.                      */
  579.                     DeleteArgstring(rmsg->rm_Args[0]);
  580.                     DeleteRexxMsg(rmsg);
  581.                 }
  582.                 Permit();
  583.             }
  584.             else DeleteRexxMsg(rmsg);
  585.         }
  586.     }
  587.     return(flag);
  588. }
  589.  
  590. /*
  591.  * This function closes down the ARexx context that was opened
  592.  * with InitARexx...
  593.  */
  594. void FreeARexx(AREXXCONTEXT RexxContext)
  595. {
  596. register    struct    RexxMsg    *rmsg;
  597.  
  598.     if (RexxContext)
  599.     {
  600.         /*
  601.          * Clear port name so it can't be found...
  602.          */
  603.         RexxContext->PortName[0]='\0';
  604.  
  605.         /*
  606.          * Clean out any outstanding messages we had sent out...
  607.          */
  608.         while (RexxContext->Outstanding)
  609.         {
  610.             WaitPort(RexxContext->ARexxPort);
  611.             while (rmsg=GetARexxMsg(RexxContext))
  612.             {
  613.                 if (rmsg!=REXX_RETURN_ERROR)
  614.                 {
  615.                     /*
  616.                      * Any messages that come now are blown
  617.                      * away...
  618.                      */
  619.                     SetARexxLastError(RexxContext,rmsg,
  620.                                 "99: Port Closed!");
  621.                     ReplyARexxMsg(RexxContext,rmsg,
  622.                             NULL,100);
  623.                 }
  624.             }
  625.         }
  626.  
  627.         /*
  628.          * Clean up the port and delete it...
  629.          */
  630.         if (RexxContext->ARexxPort)
  631.         {
  632.             while (rmsg=GetARexxMsg(RexxContext))
  633.             {
  634.                 /*
  635.                  * Any messages that still are coming in are
  636.                  * "dead"  We just set the LASTERROR and
  637.                  * reply an error of 100...
  638.                  */
  639.                 SetARexxLastError(RexxContext,rmsg,
  640.                             "99: Port Closed!");
  641.                 ReplyARexxMsg(RexxContext,rmsg,NULL,100);
  642.             }
  643.             DeletePort(RexxContext->ARexxPort);
  644.         }
  645.  
  646.         /*
  647.          * Make sure we close the library...
  648.          */
  649.         if (RexxContext->RexxSysBase)
  650.         {
  651.             CloseLibrary(RexxContext->RexxSysBase);
  652.         }
  653.  
  654.         /*
  655.          * Free the memory of the RexxContext
  656.          */
  657.         FreeMem(RexxContext,sizeof(struct ARexxContext));
  658.     }
  659. }
  660.  
  661. /*
  662.  * This routine initializes an ARexx port for your process
  663.  * This should only be done once per process.  You must call it
  664.  * with a valid application name and you must use the handle it
  665.  * returns in all other calls...
  666.  *
  667.  * NOTE:  The AppName should not have spaces in it...
  668.  *        Example AppNames:  "MyWord" or "FastCalc" etc...
  669.  *        The name *MUST* be less that 16 characters...
  670.  *        If it is not, it will be trimmed...
  671.  *        The name will also be UPPER-CASED...
  672.  *
  673.  * NOTE:  The Default file name extension, if NULL will be
  674.  *        "rexx"  (the "." is automatic)
  675.  */
  676. AREXXCONTEXT InitARexx(char *AppName,char *Extension)
  677. {
  678. register    AREXXCONTEXT    RexxContext=NULL;
  679. register    short        loop;
  680. register    short        count;
  681. register    char        *tmp;
  682.  
  683.     if (RexxContext=AllocMem(sizeof(struct ARexxContext),
  684.                     MEMF_PUBLIC|MEMF_CLEAR))
  685.     {
  686.         if (RexxContext->RexxSysBase=OpenLibrary("rexxsyslib.library",
  687.                                 NULL))
  688.         {
  689.             /*
  690.              * Set up the extension...
  691.              */
  692.             if (!Extension) Extension="rexx";
  693.             tmp=RexxContext->Extension;
  694.             for (loop=0;(loop<7)&&(Extension[loop]);loop++)
  695.             {
  696.                 *tmp++=Extension[loop];
  697.             }
  698.             *tmp='\0';
  699.  
  700.             /*
  701.              * Set up a port name...
  702.              */
  703.             tmp=RexxContext->PortName;
  704.             for (loop=0;(loop<16)&&(AppName[loop]);loop++)
  705.             {
  706.                 *tmp++=toupper(AppName[loop]);
  707.             }
  708.             *tmp='\0';
  709.  
  710.             /*
  711.              * Set up the last error RVI name...
  712.              *
  713.              * This is <appname>.LASTERROR
  714.              */
  715.             strcpy(RexxContext->ErrorName,RexxContext->PortName);
  716.             strcat(RexxContext->ErrorName,".LASTERROR");
  717.  
  718.             /* We need to make a unique port name... */
  719.             Forbid();
  720.             for (count=1,RexxContext->ARexxPort=(VOID *)1;
  721.                         RexxContext->ARexxPort;count++)
  722.             {
  723.                 stci_d(tmp,count);
  724.                 RexxContext->ARexxPort=
  725.                         FindPort(RexxContext->PortName);
  726.             }
  727.  
  728.             RexxContext->ARexxPort=CreatePort(
  729.                         RexxContext->PortName,NULL);
  730.             Permit();
  731.         }
  732.  
  733.         if (    (!(RexxContext->RexxSysBase))
  734.              ||    (!(RexxContext->ARexxPort))    )
  735.         {
  736.             FreeARexx(RexxContext);
  737.             RexxContext=NULL;
  738.         }
  739.     }
  740.     return(RexxContext);
  741. }
  742.  
  743.  
  744.  
  745.  
  746. /*
  747.  * File: SimpleRexxExample.c
  748.  *
  749.  * This is an example of how to use the SimpleRexx code.
  750.  */
  751.  
  752. #include    <exec/types.h>
  753. #include    <libraries/dos.h>
  754. #include    <libraries/dosextens.h>
  755. #include    <intuition/intuition.h>
  756.  
  757. #include    <proto/exec.h>
  758. #include    <proto/intuition.h>
  759.  
  760. #include    <rexx/storage.h>
  761. #include    <rexx/rxslib.h>
  762.  
  763. #include    <stdio.h>
  764. #include    <string.h>
  765.  
  766. #include    "SimpleRexx.h"
  767.  
  768. /*
  769.  * Lattice control-c stop...
  770.  */
  771. int CXBRK(void) { return(0); }  /* Disable Lattice CTRL/C handling */
  772. int chkabort(void) { return(0); }  /* really */
  773.  
  774. /*
  775.  * The strings in this program
  776.  */
  777. char *strings[]=
  778. {
  779.     "50: Window already open",        /* STR_ID_WINDOW_OPEN */
  780.     "101: Window did not open",        /* STR_ID_WINDOW_ERROR */
  781.     "50: No Window",            /* STR_ID_WINDOW_NONE */
  782.     "80: Argument error to WINDOW command",    /* STR_ID_WINDOW_ARG */
  783.     "100: Unknown command",            /* STR_ID_COMMAND_ERROR */
  784.     "ARexx port name: %s\n",        /* STR_ID_PORT_NAME */
  785.     "No ARexx on this system.\n",        /* STR_ID_NO_AREXX */
  786.     "SimpleRexxExample Window"        /* STR_ID_WINDOW_TITLE */
  787. };
  788.  
  789. #define    STR_ID_WINDOW_OPEN    0
  790. #define    STR_ID_WINDOW_ERROR    1
  791. #define    STR_ID_WINDOW_NONE    2
  792. #define    STR_ID_WINDOW_ARG    3
  793. #define    STR_ID_COMMAND_ERROR    4
  794. #define    STR_ID_PORT_NAME    5
  795. #define    STR_ID_NO_AREXX        6
  796. #define    STR_ID_WINDOW_TITLE    7
  797.  
  798. /*
  799.  * NewWindow structure...
  800.  */
  801. static struct NewWindow nw=
  802. {
  803.     97,47,299,44,-1,-1,
  804.     CLOSEWINDOW,
  805.     WINDOWSIZING|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SIMPLE_REFRESH|NOCAREREFRESH,
  806.     NULL,NULL,
  807.     NULL,
  808.     NULL,NULL,290,40,-1,-1,WBENCHSCREEN
  809. };
  810.  
  811. /*
  812.  * A *VERY* simple and simple-minded example of using the SimpleRexx.c code.
  813.  *
  814.  * This program, when run, will print out the name of the ARexx port it
  815.  * opens.  Use that port to tell it to SHOW the window.  You can also
  816.  * use the ARexx port to HIDE the window, to READTITLE the window's
  817.  * titlebar, and QUIT the program.  You can also quit the program by
  818.  * pressing the close gadget in the window while the window is up.
  819.  *
  820.  * Note: You will want to RUN this program or have another shell available such
  821.  *       that you can still have access to ARexx...
  822.  */
  823. void main(int argc,char *argv[])
  824. {
  825. short    loopflag=TRUE;
  826. AREXXCONTEXT    RexxStuff;
  827. struct    Window    *win=NULL;
  828. ULONG    signals;
  829.  
  830.     if (IntuitionBase=(struct IntuitionBase *)
  831.                     OpenLibrary("intuition.library",NULL))
  832.     {
  833.         /*
  834.          * Note that SimpleRexx is set up such that you do not
  835.          * need to check for an error to initialize your REXX port
  836.          * This is so your application could run without REXX...
  837.          */
  838.         RexxStuff=InitARexx("Example","test");
  839.  
  840.         if (argc)
  841.         {
  842.             if (RexxStuff) printf(strings[STR_ID_PORT_NAME],
  843.                             ARexxName(RexxStuff));
  844.             else printf(strings[STR_ID_NO_AREXX]);
  845.         }
  846.  
  847.         while (loopflag)
  848.         {
  849.             signals=ARexxSignal(RexxStuff);
  850.             if (win) signals|=(1L << (win->UserPort->mp_SigBit));
  851.  
  852.             if (signals)
  853.             {
  854.             struct    RexxMsg        *rmsg;
  855.             struct    IntuiMessage    *msg;
  856.  
  857.                 signals=Wait(signals);
  858.  
  859.                 /*
  860.                  * Process the ARexx messages...
  861.                  */
  862.                 while (rmsg=GetARexxMsg(RexxStuff))
  863.                 {
  864.                 char    cBuf[24];
  865.                 char    *nextchar;
  866.                 char    *error=NULL;
  867.                 char    *result=NULL;
  868.                 long    errlevel=0;
  869.  
  870.                     nextchar=stptok(ARG0(rmsg),
  871.                                 cBuf,24," ,");
  872.                     if (*nextchar) nextchar++;
  873.  
  874.                     if (!stricmp("WINDOW",cBuf))
  875.                     {
  876.                         if (!stricmp("OPEN",nextchar))
  877.                         {
  878.                             if (win)
  879.                             {
  880.                                 error=strings[STR_ID_WINDOW_OPEN];
  881.                                 errlevel=5;
  882.                             }
  883.                             else
  884.                             {
  885.                                 nw.Title=strings[STR_ID_WINDOW_TITLE];
  886.                                 if (!(win=OpenWindow(&nw)))
  887.                                 {
  888.                                     error=strings[STR_ID_WINDOW_ERROR];
  889.                                     errlevel=30;
  890.                                 }
  891.                             }
  892.                         }
  893.                         else if (!stricmp("CLOSE",nextchar))
  894.                         {
  895.                             if (win)
  896.                             {
  897.                                 CloseWindow(win);
  898.                                 win=NULL;
  899.                             }
  900.                             else
  901.                             {
  902.                                 error=strings[STR_ID_WINDOW_NONE];
  903.                                 errlevel=5;
  904.                             }
  905.                         }
  906.                         else
  907.                         {
  908.                             error=strings[STR_ID_WINDOW_ARG];
  909.                             errlevel=20;
  910.                         }
  911.                     }
  912.                     else if (!stricmp("READTITLE",cBuf))
  913.                     {
  914.                         if (win)
  915.                         {
  916.                             result=win->Title;
  917.                         }
  918.                         else
  919.                         {
  920.                             error=strings[STR_ID_WINDOW_NONE];
  921.                             errlevel=5;
  922.                         }
  923.                     }
  924.                     else if (!stricmp("QUIT",cBuf))
  925.                     {
  926.                         loopflag=FALSE;
  927.                     }
  928.                     else
  929.                     {
  930.                         error=strings[STR_ID_COMMAND_ERROR];
  931.                         errlevel=20;
  932.                     }
  933.  
  934.                     if (error)
  935.                     {
  936.                         SetARexxLastError(RexxStuff,rmsg,error);
  937.                     }
  938.                     ReplyARexxMsg(RexxStuff,rmsg,result,errlevel);
  939.                 }
  940.  
  941.                 /*
  942.                  * If we have a window, process those messages
  943.                  */
  944.                 if (win) while (msg=(struct IntuiMessage *)
  945.                             GetMsg(win->UserPort))
  946.                 {
  947.                     if (msg->Class==CLOSEWINDOW)
  948.                     {
  949.                         /*
  950.                          * Quit if the close gadget...
  951.                          */
  952.                         loopflag=FALSE;
  953.                     }
  954.                     ReplyMsg((struct Message *)msg);
  955.                 }
  956.             }
  957.             else loopflag=FALSE;
  958.         }
  959.         if (win) CloseWindow(win);
  960.  
  961.         FreeARexx(RexxStuff);
  962.         CloseLibrary((struct Library *)IntuitionBase);
  963.     }
  964. }
  965.  
  966.  
  967.  
  968. /*
  969.  * File: test.rexx
  970.  *
  971.  * SimpleRexx test...
  972.  *
  973.  * You need to run the SimpleRexxExample first...
  974.  */
  975.  
  976. Options FailAt 100
  977.  
  978. Options Results
  979.  
  980. /*
  981.  * Try to read the window title bar
  982.  */
  983. Address EXAMPLE1 ReadTitle
  984.  
  985. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  986. else say 'Window title is 'Result
  987.  
  988. /*
  989.  * Bad WINDOW command...
  990.  */
  991. Address EXAMPLE1 "Window Display"
  992.  
  993. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  994. else say 'Window is now open'
  995.  
  996. /*
  997.  * Open the window
  998.  */
  999. Address EXAMPLE1 "Window Open"
  1000.  
  1001. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  1002. else say 'Window is now open'
  1003.  
  1004. /*
  1005.  * Open the window
  1006.  */
  1007. Address EXAMPLE1 "Window Open"
  1008.  
  1009. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  1010. else say 'Window is now open'
  1011.  
  1012. /*
  1013.  * Try to read the window title bar
  1014.  */
  1015. Address EXAMPLE1 ReadTitle
  1016.  
  1017. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  1018. else say 'Window title is 'Result
  1019.  
  1020. /*
  1021.  * Hide the window
  1022.  */
  1023. Address EXAMPLE1 "Window Close"
  1024.  
  1025. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  1026. else say 'Window is now closed'
  1027.  
  1028. /*
  1029.  * Try to hide the window again
  1030.  */
  1031. Address EXAMPLE1 "Window Close"
  1032.  
  1033. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  1034. else say 'Window is now closed'
  1035.  
  1036. /*
  1037.  * Send a command that does not exist
  1038.  */
  1039. Address EXAMPLE1 Junk
  1040.  
  1041. if rc > 0 then say 'Error was 'EXAMPLE.LASTERROR
  1042. else say 'The command worked!!!'
  1043.  
  1044. /*
  1045.  * Quit the program...
  1046.  */
  1047. Address EXAMPLE1 Quit
  1048.  
  1049.  
  1050.  
  1051.  
  1052. File: test.results
  1053.  
  1054. Error was 50: No Window
  1055. Error was 80: Argument error to WINDOW command
  1056. Window is now open
  1057. Error was 50: Window already open
  1058. Window title is SimpleRexxExample Window
  1059. Window is now closed
  1060. Error was 50: No Window
  1061. Error was 100: Unknown command
  1062.